package com.ld.common.webnotice.socket;

import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.commons.lang3.StringUtils;

import com.corundumstudio.socketio.AckCallback;
import com.corundumstudio.socketio.AckRequest;
import com.corundumstudio.socketio.Configuration;
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.SocketIOServer;
import com.corundumstudio.socketio.listener.DataListener;
import com.ld.common.webnotice.model.PushMessage;
import com.ld.common.webnotice.model.SocketIOMsgType;
import com.ld.common.webnotice.model.SocketIOParams;
import com.ld.shieldsb.common.core.model.PropertiesModel;

import lombok.extern.slf4j.Slf4j;

/**
 * 
 * netty-socketio工具类 创建、添加和启动客户端 消息推送 关闭服务
 * 
 * @ClassName SocketIO
 * @author <a href="mailto:donggongai@126.com" target="_blank">吕凯</a>
 * @date 2019年7月9日 下午2:08:33
 *
 */
@Slf4j
public class SocketIO implements SocketIOService {
    // 用来存已连接的客户端
    private static Map<String, SocketIOClient> clientMap = new ConcurrentHashMap<>();

    private static SocketIOServer server;

    /**
     * @Title: startSocketio
     * @Description: 创建服务添加客户端
     */
    public void startSocketio() {
        log.warn("Socketio=================start");
        // 配置
        Configuration conf = new Configuration();
        // 指定要主机ip地址，这个和页面请求ip地址一致
        conf.setHostname(PropertiesModel.CONFIG.getString(SocketIOParams.PARAM_SERVER_HOST));
        // 指定端口号
        conf.setPort(PropertiesModel.CONFIG.getInt(SocketIOParams.PARAM_SERVER_PORT, 9092));
        // 设置最大的WebSocket帧内容长度限制
        conf.setMaxFramePayloadLength(1024 * 1024);
        // 设置最大HTTP内容长度限制
        conf.setMaxHttpContentLength(1024 * 1024);

        server = new SocketIOServer(conf);

        // 添加客户端监听，此处使用了java8写法，完整的写法 new ConnectListener() { @Override public void onConnect(SocketIOClient client) {} };
        server.addConnectListener(client -> {
            log.warn(client.getRemoteAddress() + " web客户端接入" + client.getSessionId());
            client.sendEvent(SocketIOMsgType.TYPE_PUBLIC_WELCOME, "hello");
            String loginUserNum = getParamsByClient(client);
            if (loginUserNum != null) {
                clientMap.put(loginUserNum, client);
            }
        });
        server.addDisconnectListener(client -> {
            log.warn(client.getRemoteAddress() + " web客户端断开连接" + client.getSessionId());
            String loginUserNum = getParamsByClient(client);
            if (loginUserNum != null) {
                clientMap.remove(loginUserNum);
                client.disconnect();
            }
        });

        // 握手请求
        server.addEventListener("helloevent", PushMessage.class, new DataListener<PushMessage>() {
            @Override
            public void onData(final SocketIOClient client, PushMessage data, AckRequest ackRequest) {
                // 握手
                if (data.getMessage().equals("hello")) {
                    String userid = data.getFromUserId();
                    log.debug(Thread.currentThread().getName() + "web读取到的userid：" + userid);
                    if (userid != null) {

                        // send message back to client with ack callback WITH data
                        client.sendEvent("hellopush", new AckCallback<String>(String.class) {
                            @Override
                            public void onSuccess(String result) { // result为客户端的回传信息
                                log.warn("ack from client: " + client.getSessionId() + " data: " + result);
                            }
                        }, 30);
                    }

                } else {
                    log.info("行情接收到了不应该有的web客户端请求1111...");
                }
            }
        });

        server.start();
    }

    /**
     * 发送消息
     * 
     * @Title sendMessage
     * @author 吕凯
     * @date 2019年7月9日 下午2:26:09
     * @param type
     *            前台根据类型接收消息，所以接收的消息类型不同，收到的通知就不同 推送的事件类型
     * @param content
     *            推送的内容 void
     */
    public void sendMessage(String type, Object content) {

        // 获取全部客户端
        Collection<SocketIOClient> allClients = server.getAllClients();
        for (SocketIOClient socket : allClients) {
            socket.sendEvent(type, content);
        }
    }

    /**
     * 发送给用户
     * 
     * @Title pushMessageToUser
     * @author 吕凯
     * @date 2019年7月10日 上午11:19:42
     * @param pushMessage
     * @see com.ld.common.webnotice.socket.SocketIOService#pushMessageToUser(com.ld.common.webnotice.model.PushMessage)
     */
    public void pushMessageToUser(PushMessage pushMessage) {
        String userId = pushMessage.getToUserId();
        if (StringUtils.isNotBlank(userId)) {
            SocketIOClient client = clientMap.get(userId);
            if (client != null) {
                client.sendEvent(SocketIOMsgType.TYPE_PRIVATE, pushMessage.getMessage());
            }
        }
    }

    @Override
    public void pushMessageToUser(SocketIOClient client, PushMessage pushMessage) {
        client.sendEvent(SocketIOMsgType.TYPE_PRIVATE, pushMessage.getMessage());
    }

    /**
     * 发送个群组
     * 
     * @Title pushMessageToGroup
     * @author 吕凯
     * @date 2019年7月10日 上午11:19:54
     * @param pushMessage
     * @see com.ld.common.webnotice.socket.SocketIOService#pushMessageToGroup(com.ld.common.webnotice.model.PushMessage)
     */
    public void pushMessageToGroup(PushMessage pushMessage) {
        // 暂不实现
    }

    /**
     * 发送给全体
     * 
     * @Title pushMessageToAll
     * @author 吕凯
     * @date 2019年7月10日 上午11:20:14
     * @param pushMessage
     * @see com.ld.common.webnotice.socket.SocketIOService#pushMessageToAll(com.ld.common.webnotice.model.PushMessage)
     */
    public void pushMessageToAll(PushMessage pushMessage) {
        // 获取全部客户端
        Collection<SocketIOClient> allClients = server.getAllClients();
        for (SocketIOClient socket : allClients) {
            socket.sendEvent(pushMessage.getType(), pushMessage.getMessage());
        }

    }

    /**
     * 此方法为获取client连接中的参数，可根据需求更改
     * 
     * @param client
     * @return
     */
    private String getParamsByClient(SocketIOClient client) {
        // 从请求的连接中拿出参数（这里的uid必须是唯一标识）
        Map<String, List<String>> params = client.getHandshakeData().getUrlParams();
        List<String> list = params.get("uid");
        if (list != null && list.size() > 0) {
            return list.get(0);
        }
        return null;
    }

    /**
     * @Title: startServer
     * @Description: 启动服务
     */
    @Override
    public void start() {
        if (server == null) {
            new Thread(new Runnable() {

                @Override
                public void run() {
                    startSocketio();
                }
            }).start();
        }
    }

    /**
     * @Title: stopSocketio
     * @Description: 停止服务
     */
    @Override
    public void stop() {
        if (server != null) {
            server.stop();
            server = null;
        }
    }

}
